package com.onpositive.knowledge.model.value.audits;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import com.onpositive.data.IPropertyConstants;
import com.onpositive.knowledge.model.values.DescribedValue;
import com.onpositive.knowledge.model.values.ReplacementProposals;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.units.ParsedIdent;
import com.onpositive.units.ParsedString;
import com.onpositive.units.ParsedValueSequence;

public class WikiLinksStatisticAudit extends AbstractUniverseAudit {

	/**
	 * 
	 */
	private static final long serialVersionUID = 7528795246250887376L;
	public final static String ID = "reference-statistic-audit" ;
	
	public WikiLinksStatisticAudit() {
		super();
	}

	
	private final static HashSet<String> acceptedValueTypes = new HashSet<String>( 
			Arrays.asList(new String[]{ ParsedString.ID, ParsedIdent.ID, ParsedValueSequence.ID }) ) ;

	@Override
	public void perform( Iterable<IProperty> properties, Iterable<Object> objects, AbstractAuditResult auditResult) {
		
		Map<String,Object> metaInfo = new HashMap<String, Object>() ;
		metaInfo.put("audit-result", auditResult) ;
		
		iterateProperties(properties, objects, metaInfo, 0);
		
		processStats( (WikiLinksDisambiguationAuditResult) auditResult ) ;
	
		auditResult.setLooksApplyable(true) ;
	}


	@Override
	public void iterateProperties(Iterable<IProperty> properties, Iterable<Object> objects, Map<String,Object> metaInfo, int ind)
	{
		Object resultObject = metaInfo.get("audit-result");
		if( resultObject == null || !(resultObject instanceof WikiLinksDisambiguationAuditResult) )
			return ;
		
		WikiLinksDisambiguationAuditResult mainResult = (WikiLinksDisambiguationAuditResult)resultObject;
		
		for( IProperty prop : properties ){
			
			String name = prop.getMeta().getSingleValue( "id", String.class, null ) ;
			
			
			Boolean isService = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_IS_SERVICE_PROPERTY, Boolean.class, null) ;
			if( isService != null && isService )
				continue ;
			
			Boolean isDepricated = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_DEPRICATED_LABEL, Boolean.class, null);
			if( isDepricated != null && isDepricated )
				continue ;			
			
			WikiLinksDisambiguationAuditResult pResult = new WikiLinksDisambiguationAuditResult( prop ) ;
			metaInfo.put("per-property-audit-result", pResult) ;
			
			iterateObjects( prop, objects, metaInfo, ind );
			
			contribute( pResult, mainResult) ;
			mainResult.putPropertyResult( name, pResult ) ;
			processStats( pResult ) ;
			pResult.setLooksApplyable(true) ;	
		}
	}
	
	@Override
	protected void process(IProperty prop, Object val, Map<String,Object> metaInfo, int index) {
		
		Object resultObject = metaInfo.get("per-property-audit-result");
		if( resultObject == null || !(resultObject instanceof WikiLinksDisambiguationAuditResult) )
			return ;
		
		WikiLinksDisambiguationAuditResult result = (WikiLinksDisambiguationAuditResult)resultObject;
		
		if( index == 0 )
			fillStats(val, (WikiLinksDisambiguationAuditResult) result ) ;		
	}

	private void contribute( WikiLinksDisambiguationAuditResult pResult, WikiLinksDisambiguationAuditResult result)
	{	
		contribute( pResult.getLinksMap(), result.getLinksMap() ) ;
		contribute( pResult.getCaptionsMap(), result.getCaptionsMap() ) ;
		contribute( pResult.getLcStatsMap(), result.getLcStatsMap() ) ;
		contribute( pResult.getClStatsMap(), result.getClStatsMap() ) ;
		
		complexContibute( pResult.getLinksCaseStatsMap(), result.getLinksCaseStatsMap() ) ;
		complexContibute( pResult.getCaptionCaseStatsMap(), result.getCaptionCaseStatsMap() ) ;
	}



	private <T1,T2> void complexContibute(
			HashMap<T1, HashMap<T2, Integer>> src,
			HashMap<T1, HashMap<T2, Integer>> dst ) {
		
		for( Map.Entry<T1,HashMap<T2,Integer>> entry : src.entrySet() )
		{
			T1 key = entry.getKey() ;
			HashMap<T2,Integer> srcMap = entry.getValue() ;
			HashMap<T2,Integer> dstMap = dst.get(key) ;
			
			if( dstMap == null )
				dst.put( key, srcMap ) ;
			else
				contribute( srcMap, dstMap ) ;			
		}
		
	}



	private void processStats( WikiLinksDisambiguationAuditResult result)
	{
		HashMap<String,Integer> captionsMap_lc = result.getCaptionsMap() ;
		HashMap<String,Integer> linksMap_lc = result.getLinksMap() ;
		
		HashMap<String,Integer> clStatsMap = result.getClStatsMap() ;
		HashMap<String,Integer> lcStatsMap = result.getLcStatsMap() ;
		
//		Integer i0 = lcStatsMap.get("Naval aviation|Naval fighter") ;
//		Integer i1 = lcStatsMap.get("naval aviation|Naval fighter") ;
		
		HashMap<String,String> clMap = result.getClMap() ;
		HashMap<String,String> lcMap = result.getLcMap() ;
		
		extract(lcStatsMap, lcMap, captionsMap_lc) ;
		extract(clStatsMap, clMap, linksMap_lc ) ;
		
		Integer fianCount_lnk = linksMap_lc.get("fiat") ;
		Integer fianACount_lnk = linksMap_lc.get("fiat aviazione") ;
		Integer fianCount_cap = captionsMap_lc.get("carrierborne") ;
		Integer fianACount_cap = captionsMap_lc.get("fiat aviazione") ;
		
		close( lcMap, clMap, linksMap_lc, captionsMap_lc ) ;
		
		String capForFiat = lcMap.get("") ;
		String lnkForFiat = clMap.get("carrierborne") ;
		
		
		HashMap<String, HashMap<String,Integer>> captionCaseStatsMap = result.getCaptionCaseStatsMap() ;
		HashMap<String, HashMap<String,Integer>> linksCaseStatsMap = result.getLinksCaseStatsMap() ;
		
		HashMap<String,String> captionCaseMap = result.getCaptionCaseMap() ;
		HashMap<String,String> linksCaseMap = result.getLinksCaseMap() ;
		extract( captionCaseStatsMap, captionCaseMap ) ;
		extract( linksCaseStatsMap, linksCaseMap ) ;
		
		DisambiguationValueAudit captionsDisambiguationAudit = new DisambiguationValueAudit();
		ReplacementProposals captionProposals = new ReplacementProposals(null) ;
		captionProposals.setValidityBarrier(15) ;
		captionsDisambiguationAudit.doProcessStats( null, captionProposals, captionsMap_lc) ;
		
		DisambiguationValueAudit linksDisambiguationAudit = new DisambiguationValueAudit(){
			
			private static final long serialVersionUID = 9085245755233448594L;

			protected int compare( Object obj1, Object obj2  )
			{
				String str1 = DisambiguationValueAudit.prepareString( obj1 ) ;
				String str2 = DisambiguationValueAudit.prepareString( obj2 ) ;
				
				if( str1 == null || str2 == null )
					return -1 ;
				
				if( str1.equals(str2) )
					return -1 ;
				
				if( str1.toLowerCase().equals(str2.toLowerCase() ) )
					return 0 ;
				
				return -1 ;
			}
			
		};
		linksDisambiguationAudit.setResultSource(getResultSource()) ;
		ReplacementProposals linkProposals = new ReplacementProposals(null) ;
		linksDisambiguationAudit.doProcessStats( null, linkProposals, linksMap_lc) ;
//		
//		result.setCaptionProposals(captionProposals) ;
//		result.setLinkProposals(linkProposals) ;
				
		result.disposeTemporaryMaps() ;
	}
	
	private <T>void close( HashMap<T,T> map_a, HashMap<T,T> map_b, HashMap<T,Integer> statsMap_a, HashMap<T,Integer> statsMap_b )
	{
	
		HashSet<T> processed_a = new HashSet<T>() ;
		HashSet<T> processed_b = new HashSet<T>() ;		
		
		pass( map_a, map_b, processed_a, processed_b, statsMap_a, statsMap_b ) ;
		pass( map_b, map_a, processed_b, processed_a, statsMap_b, statsMap_a ) ;
	}
	
	private <T>void pass( HashMap<T,T> _map_a, HashMap<T,T> _map_b,
						  HashSet<T> _processed_a, HashSet<T> _processed_b,
						  HashMap<T,Integer> _statsMap_a, HashMap<T,Integer> _statsMap_b )
	{
		Collection<T> keys = _map_a.keySet() ;
		
		HashMap<T,T> map_a, map_b, map_exchange ;
		HashSet<T> processed_a, processed_b, processed_exchange ;
		LinkedHashSet<T> buf_a, buf_b, buf_exchange ;		
		LinkedHashSet<T> _buf_a = new LinkedHashSet<T>() ;
		LinkedHashSet<T> _buf_b = new LinkedHashSet<T>() ;
		HashMap<T,Integer> statsMap_a, statsMap_b, statsMap_exchange ;
		
		for( T _key : keys )
		{
			T key = _key ;
			System.out.print( "\n\n" ) ;
			//old recursive version
			//iterate( key, map_a, map_b, processed_a, processed_b, buf_a, buf_b, statsMap_a, statsMap_b ) ;
			map_a = _map_a ;
			map_b = _map_b ;
			processed_a = _processed_a ;
			processed_b = _processed_b ;
			buf_a = _buf_a;
			buf_b = _buf_b;
			statsMap_a = _statsMap_a ;
			statsMap_b = _statsMap_b ;
			
			boolean stop = false ;
			while( !stop )
			{
				if(key==null){
					stop=true;
					continue;
				}
				System.out.print( key.toString() +'\n' ) ;
				T val = map_a.get(key) ;
				boolean bufContains = buf_a.contains(key) ;
				boolean processedContains = processed_a.contains( key );						
				stop = bufContains || processedContains;
				if( stop )
				{
					T bestKey = processedContains ? key : chooseBest( key, buf_a, statsMap_a );
					T bestVal = processedContains ? val : chooseBest( val, buf_b, statsMap_b );			
					switchValueToBest(map_a, buf_a, bestVal);
					switchValueToBest(map_b, buf_b, bestKey);
					processed_a.addAll( buf_a ) ;
					processed_b.addAll( buf_b ) ;
					buf_a.clear() ;
					buf_b.clear() ;
				}
				else{
					buf_a.add(key) ;
					
					key = val ;
					
					map_exchange = map_a ;
					map_a = map_b ;
					map_b = map_exchange ;
					
					processed_exchange = processed_a ;
					processed_a = processed_b ;
					processed_b = processed_exchange ;
					
					buf_exchange = buf_a ;
					buf_a = buf_b ;
					buf_b = buf_exchange ;
					
					statsMap_exchange = statsMap_a ;
					statsMap_a = statsMap_b ;
					statsMap_b = statsMap_exchange ;
					//old recursive version
					//iterate( val, map_b, map_a, processed_b, processed_a, buf_b, buf_a, statsMap_b, statsMap_a ) ;
				}
			}
		}
	}	

	private final <T> void switchValueToBest(HashMap<T, T> map,	LinkedHashSet<T> buf, T obj)
	{
		for( T t : buf )
			map.put( t, obj ) ;	
	}
	
	private final <T> T chooseBest(T repeated, LinkedHashSet<T> buf, HashMap<T, Integer> statsMap)
	{
		T best = null ;
		int maxCount = 0 ;
		boolean found = false ;
		for( T t : buf )
		{
			found = found || t.equals( repeated ) ;
			if( !found )
				continue ;
			
			Integer count = statsMap.get(t) ;
			if( count == null )
				continue ;
			
			if( count > maxCount )
			{
				maxCount = count ;
				best = t ;
			}			
		}
		return best ;
	}
	



	private void extract( HashMap<String, HashMap<String, Integer>> statsMap, HashMap<String, String> map )
	{
		for( Map.Entry<String, HashMap<String,Integer>> entry : statsMap.entrySet() )
		{
			String key = entry.getKey() ;
			HashMap<String,Integer> currentStatsMap = entry.getValue() ;
			String maxString = null ;
			int maxCount = 0 ;
			for( Map.Entry<String, Integer> e : currentStatsMap.entrySet() )
			{
				String key1 = e.getKey() ;
				Integer count = e.getValue() ;
				if( key1 == null || count == null )
					continue ;
				
				if( count > maxCount )
				{
					maxCount = count ;
					maxString = key1 ;
				}
			}
			if( maxString != null )
				map.put(key, maxString) ;
		}		
	}



	private void extract( HashMap<String, Integer> correspondancyCountMap, HashMap<String,String> map, HashMap<String, Integer> valueCountMap )
	{
		HashMap<String,Integer> currentCountMap = new HashMap<String, Integer>() ;
		
		for( Map.Entry<String, Integer> entry : correspondancyCountMap.entrySet() )
		{
			String str = entry.getKey() ;
			Integer count = entry.getValue() ;
			
			int ind = str.indexOf( lcSeparator ) ;
			String prefix_lc = str.substring(0, ind ).trim().toLowerCase() ;
			String postfix = str.substring( ind+lcSeparator.length() ).trim() ;
			
			//lc-disambiguate postfix here;
			
			if( prefix_lc.length() == 0 || postfix.length() == 0 )
				continue ;
			
			//String currentValue = map.get(prefix) ;
			
			Integer currentCount = currentCountMap.get( prefix_lc ) ;
			if( currentCount == null ){
				map.put( prefix_lc, postfix ) ;
				currentCountMap.put( prefix_lc, count ) ;
			}
			else if(  count > currentCount )
			{
				map.put( prefix_lc, postfix ) ;
				currentCountMap.put( prefix_lc, count ) ;
			}
			else if( count == currentCount )
			{
				String currentPostfix = map.get( prefix_lc ) ;
				Integer c0 = valueCountMap.get( postfix.toLowerCase() );
				Integer c1 = valueCountMap.get( currentPostfix.toLowerCase() );
				if( c0 > c1 )
				{
					map.put( prefix_lc, postfix ) ;
					currentCountMap.put( prefix_lc, count ) ;					
				}				
			}
		}
	}

	private final static String lcSeparator = "|||" ;
	private void fillStats( Object s, WikiLinksDisambiguationAuditResult result) {

		if( !(s instanceof ParsedIdent) )
			return ;	
		
		ParsedIdent ident = (ParsedIdent) s ; 
		
		HashMap<String,Integer> linksMap = result.getLinksMap() ;
		HashMap<String,Integer> captionsMap = result.getCaptionsMap() ;
		HashMap<String,Integer> clStatsMap = result.getClStatsMap() ;
		HashMap<String,Integer> lcStatsMap = result.getLcStatsMap() ;
		HashMap<String , HashMap<String,Integer>> captionsCaseStatsMap = result.getCaptionCaseStatsMap() ;
		HashMap<String , HashMap<String,Integer>> linksCaseStatsMap = result.getLinksCaseStatsMap() ;
		
		String linkString = ident.getRef() ;
		String captionString = ident.getCaption() ;
			
		if( linkString != null )
		{	
			contribute( linkString.toLowerCase(), linksMap ) ;
			
			HashMap<String,Integer> currentLinkCaseStatsMap = linksCaseStatsMap.get( linkString.toLowerCase() ) ;
			if( currentLinkCaseStatsMap == null ){
						
				currentLinkCaseStatsMap = new HashMap<String, Integer>() ;
				linksCaseStatsMap.put( linkString.toLowerCase(), currentLinkCaseStatsMap ) ;
			}
			contribute( linkString, currentLinkCaseStatsMap );
		}
		if( captionString != null )
		{
			contribute( captionString.toLowerCase(), captionsMap ) ;
			
			HashMap<String,Integer> currentCaptionCaseStatsMap = captionsCaseStatsMap.get( captionString.toLowerCase() ) ;
			if( currentCaptionCaseStatsMap == null ){
						
				currentCaptionCaseStatsMap = new HashMap<String, Integer>() ;
				captionsCaseStatsMap.put( captionString.toLowerCase(), currentCaptionCaseStatsMap ) ;
			}
			contribute( captionString, currentCaptionCaseStatsMap );
		}
		
		if( captionString == null || linkString == null )
			return ;
			
		
		contribute( (linkString + lcSeparator + captionString).toLowerCase(), lcStatsMap ) ;
		contribute( (captionString + lcSeparator + linkString).toLowerCase() , clStatsMap ) ;
				
	}
	
	private <T>void contribute( T obj, HashMap<T,Integer> map )
	{
		Integer ind = map.get(obj)	;
		ind = ind != null ? ind + 1 : 1 ;
		map.put(obj, ind) ;
	}
	
	private <T>void contribute( HashMap<T,Integer> src, HashMap<T,Integer> dst )
	{
		for( Map.Entry<T, Integer> entry : src.entrySet() )
		{
			T key = entry.getKey();
			Integer count = entry.getValue() ;
			
			Integer ind = dst.get( key )	;
			ind = ind != null ? ind + count : count ;
			dst.put( key, ind ) ;			
		}		
	}

	@Override
	public String getId() {

		return ID;
	}


	protected boolean onStatsCompleted( AbstractAuditResult pm )
	{
		return false ;	
	}
	
	public AbstractAuditResult createAuditResult() {
		return new WikiLinksDisambiguationAuditResult( this );
	}


	protected Class<?>[] createNecessaryClasses() {
		return new Class<?>[]{ ParsedIdent.class };
	}

	protected Class<?>[] createTargetClasses() {
		return new Class<?>[]{ ParsedIdent.class };
	}


	protected void preprocessValue(Object o, DescribedValue value,
			IProperty prop, AbstractAuditResult pm)
	{
		
	}

	protected boolean isSuspicious(Object o, DescribedValue value,
			IProperty prop,AbstractAuditResult pm)
	{
		return false ;
	}

}
